/**
 * Problems:
 * - "ymsgr"? seriously?
 * 
 * - This triggers an infinite loop in IE. not funny.
 *  -> wild guess: in IE, element collections are static
 *  -> yes. fixed.
 * 
 * - very little reason to do all this "checkBody" crap when a Y.on("domready") would do the same thing, but better.
 *  -> done.
 * 
 */
YUI.add("yTags", function(Y) {
    // tag management
// everybody loves tags! (xhtml-ish, with gloriously ugly fallbacks to work anyway on non-html pages)
// in my defense, Facebook Connect and MSN Live SDK are doing it too, so it's an Industry Standard!(tm)
// (plus, it's cute, if you don't think about what's happening behind the scenes.)
// The tag detection piece is known to work on a bunch of browsers in both strict XHTML mode and HTML tagsoup mode
// Browsers include: IE6, IE7, FF3, Safari4, Chrome 1.0, Opera9.64
    
    var findTags= (function() {
	    
	    var knownNS = {};
	    
	    function getName(ns) {
	        if (knownNS[ns]) { return knownNS[ns]; }
	        // 1: figure out what name we are using for the namespace.
	        var html=document.body.parentNode,
	            attrs = document.namespaces||html.attributes,i=0,l=attrs.length;
	        for (;i<l;i++) {
	            var attr = attrs[i];
	            if (attr.value==ns || attr.urn==ns) {
	                // found. cache and return.
	                return (knownNS[ns]=attr.name.split(":").pop());
	            }
	        }
	        return null;
	    }
	
	    function findTags1(ns,tag) {
	        // non IE mode. assume XHTML, and fallback to broken tag soup as needed.
	        var tags = document.getElementsByTagNameNS(ns, tag);
	        return tags.length?tags:document.getElementsByTagName(getName(ns)+":"+tag);
	    }
	    function findTags2(ns,tag) {
	        // IE only. match by tag only, then filter results using the namespace
	        var tmp = document.getElementsByTagName(tag), tags=[], i=0, l=tmp.length, o;
	        while(i<l) {
	            if ((o=tmp[i++]).tagUrn == ns) {
	                tags.push(o);
	            }
	        }
	        return tags;
	    }
	    return document.getElementsByTagNameNS?findTags1:findTags2;
    })();

    var componentTags = {};
    
    function findComponentTags() {
        var i,comp,tags;
        for (i in componentTags) {
            comp = componentTags[i];
            tags = findTags(comp.ns, comp.tag);
            if (tags.length>0) {
            	if (typeof comp.classRef === "string") {
            		// late binding. load now.
            		(function(comp, tags){
                		var tmp = comp.classRef.split(":"),module=tmp[0],path=tmp[1];
	            		Y.use(module, function(){
	            		    var elts = path.split("."), obj = Y, first = elts.shift();
	            		    if (first!=="Y") { obj = window[first]; }
	            		    while (elts.length) {
	            		    	obj = obj[elts.shift()];
	            		    }
	            		    // ok, we should have a proper classRef.
	            		    if (typeof obj !== "function") {
	            		    	throw new Error("tag late binding didn't resolve to a function");
	            		    }
	            		    comp.classRef = obj;
	            		    populateComponentTag(obj, comp);
	                        processTags(comp, tags);
	            		});
            		})(comp, tags);
            	} else {
                    processTags(comp, tags);
            	}
            }
        }
    }
    
    function processTags(comp, tags) {
    	var tag;
        while (tags.length) {
            tag = tags.shift?tags.shift():tags[0];

            // make sure there's an id on the tag, to be able to reference it from afar
            if (!tag.id) {
                tag.id = Y.guid(comp.prefix+"_"+comp.tag+"_");
            }

            var args = {}, extras={};
            for (var j=0;j<tag.attributes.length;j++) {
                var a = tag.attributes[j], aname = comp.caseAttrs[a.name.toLowerCase()];
               
                if (aname) {
                    var aval = a.value;
                    switch(comp.attrs[aname]) {
                        case "boolean": aval = (aval=="0"||aval=="false")?false:true; break;
                        case "number": aval = parseFloat(aval); break;
                        default:
                    }
                    args[aname] = aval;
                } else {
                   extras[a.name] = a.value;
                }
            }
            var elt = createComponent(comp.tag, comp.prefix, args, comp.classRef, tag);
            // apply extra things to the bounding box
            for (j in extras) {
                switch (j) {
                case "class":
                    elt.setAttribute(j,elt.getAttribute(j)+" "+extras[j]);
                    break;
                default:
                    elt.setAttribute(j, extras[j]);
                }
            }
            
        }
    	
    }
    
    function createComponent(name, prefix, args, classRef, replaceNode) {
    	if (args.id) {
    		// tricky. yui will create its own bounding box with a unique id
    		// and associate it with the widget, so setting the id after
    		// the fact will break some things (Widget.getByNode for example).
    		// so instead, we create the bounding box ourselves..
    		var tag = classRef.ATTRS.boundingTag?classRef.ATTRS.boundingTag.value:"div",
    		    bb = document.createElement(tag);
		    if (replaceNode) {
		      replaceNode.parentNode.replaceChild(bb, replaceNode);
		    }
    		bb.id = args.id;
    		args.boundingBox = bb;
    	}
        var widget = new classRef(args);
        widget.render();
        var bbox = widget.get("boundingBox");
        return Y.Node.getDOMNode(bbox);
    }

    function registerComponentTag(ns, tag, prefix, classRef) {
        // assign
        var obj = {ns:ns, tag:tag, prefix:prefix, classRef:classRef};
        if (typeof classRef !== "string") {
        	populateComponentTag(classRef, obj);
        }
        componentTags[ns+"#"+tag] = obj;
    }
    
    function populateComponentTag(classRef, obj) {
        // loop through ATTRS, and extract attribute types and cases. useful to parse html tagsoup later.
        var c = classRef, attrs = {}, caseAttrs = {};
        while (c&&c.ATTRS) {
            for (var k in c.ATTRS) {
                var l=k.toLowerCase();
                attrs[k] = typeof c.ATTRS[k].value;
                caseAttrs[l] = k;
            }
            c=c.superclass;
            if (c) {
                c=c.constructor;
            }
        }
        // add "id", because it's a magic attribute of some sort. XXX not awesome.
        attrs.id="string";
        caseAttrs.id="id";
        // populate obj accordingly.
        obj.attrs = attrs;
        obj.caseAttrs = caseAttrs;
        return obj;
    }
    
    Y.Tags = function(prefix, ns, tags) {
        for (var tag in tags) {
            registerComponentTag(ns, tag, prefix, tags[tag]);
        }
        Y.on("domready",findComponentTags);
        return {
            refresh: function() {
                Y.on("domready",findComponentTags);
            }
        };
    };
}, "0.1");
